/*
 *   ControlUI.cpp
 *
 *	 Copyright(c) 2012 by Joe Kilner
 *   This file is part of Panoptik.
 *
 *   Panoptik is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Panoptik is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with Panoptik.  If not, see <http://www.gnu.org/licenses/>.
 */

// -*- C++ -*- generated by wxGlade 0.6.3 on Tue Jan 17 14:53:59 2012

#include "ControlUI.h"
#include "../Panoptik.h"
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <OpenThreads/Thread>

using namespace std;
using namespace zmq;

// begin wxGlade: ::extracode

// end wxGlade

#include <stdio.h>
#include <unistd.h>
#include <string.h>

enum thread_action{
	master_save,
	master_save_all,
	slave_wait
};

class ControlThread : public OpenThreads::Thread {

public:
	ControlThread(ControlUI *pControl, thread_action state):
		_pControl(pControl),
		_state(state)
	{
	}

	// Override. Run the thread
	virtual void run() {
		if(_state == master_save) {
			_pControl->threadSaveMaster();
		}
		if(_state == master_save_all) {
			_pControl->threadSaveAllMaster();
		}
		if(_state == slave_wait) {
			_pControl->threadRunSlave();
		}
	}

private:
	ControlUI *_pControl;
	thread_action _state;
};

ControlUI::ControlUI(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style):
    wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE),
    _netState(solo),
    _pSocket(0)
{
    // begin wxGlade: ControlUI::ControlUI
    control_menubar = new wxMenuBar();
    wxMenu* wxglade_tmp_menu_1 = new wxMenu();
    wxglade_tmp_menu_1->Append(menu_quit, wxT("Quit"), wxEmptyString, wxITEM_NORMAL);
    control_menubar->Append(wxglade_tmp_menu_1, wxT("File"));
    SetMenuBar(control_menubar);
    save_all_action = new wxButton(this, action_save_all, wxT("Save All Sequential"));
    save_all_path = new wxTextCtrl(this, wxID_ANY, wxT("/home/scanneruser/Calibration/calibration_meshes/cal_"));
    save_all_browser = new wxButton(this, action_save_all_browse, wxT("..."));
    save_action = new wxButton(this, action_save, wxT("Save All Concurrent"));
    save_path = new wxTextCtrl(this, wxID_ANY, wxT("/home/scanneruser/Capture/test.obj"));
    save_browser = new wxButton(this, action_save_browse, wxT("..."));
    master_action = new wxButton(this, action_master, wxT("Run As Master"));
    master_port = new wxTextCtrl(this, wxID_ANY, wxT("60666"));
    slave_action = new wxButton(this, action_slave, wxT("Run As Slave"));
    slave_port = new wxTextCtrl(this, wxID_ANY, wxT("60666"));
    slave_ip = new wxTextCtrl(this, wxID_ANY, wxT("iffrit.local"));
    load_calibration_action = new wxButton(this, action_load_calibration, wxT("Load Calibration"));
    load_calibration_path = new wxTextCtrl(this, wxID_ANY, wxT("/home/scanneruser/Calibration/cal_"));
    load_calibration_browser = new wxButton(this, action_load_calibration_browse, wxT("..."));
    device_label_1 = new wxStaticText(this, wxID_ANY, wxT("Device ID 0 -> Physical ID"));
    const wxString name_combo_1_choices[] = {
        wxT("0"),
        wxT("1"),
        wxT("2"),
        wxT("3"),
        wxT("4"),
        wxT("5"),
        wxT("6"),
        wxT("7")
    };
    name_combo_1 = new wxComboBox(this, action_name_combo_1, wxT(""), wxDefaultPosition, wxDefaultSize, 8, name_combo_1_choices, wxCB_DROPDOWN|wxCB_READONLY|wxCB_SORT);
    checkbox_1 = new wxCheckBox(this, action_checkbox_1, wxT("Enable"));
    device_label_2 = new wxStaticText(this, wxID_ANY, wxT("Device ID 1 -> Physical ID"));
    const wxString name_combo_2_choices[] = {
        wxT("0"),
        wxT("1"),
        wxT("2"),
        wxT("3"),
        wxT("4"),
        wxT("5"),
        wxT("6"),
        wxT("7")
    };
    name_combo_2 = new wxComboBox(this, action_name_combo_2, wxT(""), wxDefaultPosition, wxDefaultSize, 8, name_combo_2_choices, wxCB_DROPDOWN|wxCB_READONLY|wxCB_SORT);
    checkbox_2 = new wxCheckBox(this, action_checkbox_2, wxT("Enable"));
    device_label_3 = new wxStaticText(this, wxID_ANY, wxT("Device ID 2 -> Physical ID:"));
    const wxString name_combo_3_choices[] = {
        wxT("0"),
        wxT("1"),
        wxT("2"),
        wxT("3"),
        wxT("4"),
        wxT("5"),
        wxT("6"),
        wxT("7")
    };
    name_combo_3 = new wxComboBox(this, action_name_combo_3, wxT(""), wxDefaultPosition, wxDefaultSize, 8, name_combo_3_choices, wxCB_DROPDOWN|wxCB_READONLY|wxCB_SORT);
    checkbox_3 = new wxCheckBox(this, action_checkbox_3, wxT("Enable"));
    device_label_4 = new wxStaticText(this, wxID_ANY, wxT("Device ID 3 -> Physical ID:"));
    const wxString name_combo_4_choices[] = {
        wxT("0"),
        wxT("1"),
        wxT("2"),
        wxT("3"),
        wxT("4"),
        wxT("5"),
        wxT("6"),
        wxT("7")
    };
    name_combo_4 = new wxComboBox(this, action_name_combo_4, wxT(""), wxDefaultPosition, wxDefaultSize, 8, name_combo_4_choices, wxCB_DROPDOWN|wxCB_READONLY|wxCB_SORT);
    checkbox_4 = new wxCheckBox(this, action_checkbox_4, wxT("Enable"));
    panel_1 = new wxPanel(this, wxID_ANY);

    set_properties();
    do_layout();
    // end wxGlade
}

ControlUI::~ControlUI()
{
    if (_pSocket) {
    	if(_netState == master) {
    		message_t end_rep (3);
    		memcpy((void *) end_rep.data (), "END", 3);
    		cout << "sending END " << endl;
    		_pSocket->send(end_rep);
    	}
    	delete _pSocket;
    	_pSocket = 0;
    }
}

BEGIN_EVENT_TABLE(ControlUI, wxFrame)
    // begin wxGlade: ControlUI::event_table
    EVT_MENU(menu_quit, ControlUI::onQuitApp)
    EVT_BUTTON(action_save_all, ControlUI::onSaveAll)
    EVT_BUTTON(action_save_all_browse, ControlUI::onSaveAllBrowse)
    EVT_BUTTON(action_save, ControlUI::onSave)
    EVT_BUTTON(action_save_browse, ControlUI::onSaveBrowse)
    EVT_BUTTON(action_master, ControlUI::onRunMaster)
    EVT_BUTTON(action_slave, ControlUI::onRunSlave)
    EVT_BUTTON(action_load_calibration, ControlUI::onLoadCalibration)
    EVT_BUTTON(action_load_calibration_browse, ControlUI::onLoadCalibrationBrowse)
    EVT_COMBOBOX(action_name_combo_1, ControlUI::onDevice1NameChange)
    EVT_CHECKBOX(action_checkbox_1, ControlUI::onDevice1Enable)
    EVT_COMBOBOX(action_name_combo_2, ControlUI::onDevice2NameChange)
    EVT_CHECKBOX(action_checkbox_2, ControlUI::onDevice2Enable)
    EVT_COMBOBOX(action_name_combo_3, ControlUI::onDevice3NameChange)
    EVT_CHECKBOX(action_checkbox_3, ControlUI::onDevice3Enable)
    EVT_COMBOBOX(action_name_combo_4, ControlUI::onDevice4NameChange)
    EVT_CHECKBOX(action_checkbox_4, ControlUI::onDevice4Enable)
    // end wxGlade
END_EVENT_TABLE();


void ControlUI::onQuitApp(wxCommandEvent &event)
{
	Close();
}


void ControlUI::onSaveAll(wxCommandEvent &event)
{
	wxBeginBusyCursor();
	string out_path(save_all_path->GetValue().char_str());
	if(_netState == solo ) {
		_pApp->save(out_path,true);
		wxEndBusyCursor();
	}
	if(_netState == master) {
		if (_pThread != NULL) {
			_pThread->cancel();
			delete _pThread;
			_pThread = NULL;
		}
		_pThread = new ControlThread(this,master_save_all);
		this->Disable();
		_pThread->start();
	}
}


void ControlUI::onSaveAllBrowse(wxCommandEvent &event)
{
	save_all_path->SetValue(wxSaveFileSelector(wxT("Save File Name"), wxT(""), save_all_path->GetValue(), this));
}


void ControlUI::onSave(wxCommandEvent &event)
{
	wxBeginBusyCursor();
	string out_path(save_path->GetValue().char_str());
	if(_netState == solo) {
		_pApp->save(out_path,false);
		wxEndBusyCursor();
	}
	if(_netState == master) {
		if (_pThread != NULL) {
			_pThread->cancel();
			delete _pThread;
			_pThread = NULL;
		}
		_pThread = new ControlThread(this,master_save);
		this->Disable();
		_pThread->start();
	}
}


void ControlUI::onSaveBrowse(wxCommandEvent &event)
{
	save_path->SetValue(wxSaveFileSelector(wxT("Save File Name"), wxT(".obj"), save_path->GetValue(), this));
}


void ControlUI::onRunMaster(wxCommandEvent &event)
{
	if (_netState != master) {
		try {
			//  Socket to talk to clients
			_pSocket = new socket_t(_pApp->getZMQContext(), ZMQ_REQ);

			stringstream socket_def;
			socket_def << "tcp://*:" << master_port->GetValue().char_str();
			cout << "Binding to: " << socket_def.str() << endl;
			_pSocket->bind(socket_def.str().c_str());
			_netState = master;
			master_port->Disable();
			slave_action->Disable();
			slave_port->Disable();
			slave_ip->Disable();
		}
		catch (zmq::error_t &error) {
			cout << "Network Error:" << endl;
			cout << error.what() << endl;
		}
	}
	else {
		message_t end_rep (3);
		memcpy((void *) end_rep.data (), "END", 3);
		cout << "sending END " << endl;
		_pSocket->send(end_rep);
		_pSocket->close();
    	delete _pSocket;
    	_pSocket = 0;
		_netState = solo;
		master_port->Enable();
		slave_action->Enable();
		slave_port->Enable();
		slave_ip->Enable();
	}
}


void ControlUI::onRunSlave(wxCommandEvent &event)
{
	wxBeginBusyCursor();
	if (_pThread != NULL) {
		_pThread->cancel();
		delete _pThread;
		_pThread = NULL;
	}
	_pThread = new ControlThread(this,slave_wait);
	this->Disable();
	_pThread->start();
}


void ControlUI::onLoadCalibration(wxCommandEvent &event)
{
	string load_path(load_calibration_path->GetValue().char_str());
	_pApp->loadDepthCalibration(load_path);
}


void ControlUI::onLoadCalibrationBrowse(wxCommandEvent &event)
{
	load_calibration_path->SetValue(wxSaveFileSelector(wxT("Load File Name"), wxT(""), load_calibration_path->GetValue(), this));
}


void ControlUI::onDevice1NameChange(wxCommandEvent &event)
{
	_pApp->setName(0, string(name_combo_1->GetValue().char_str()));
}


void ControlUI::onDevice1Enable(wxCommandEvent &event)
{
	_pApp->enableDevice(0,checkbox_1->GetValue());
}


void ControlUI::onDevice2NameChange(wxCommandEvent &event)
{
	_pApp->setName(1, string(name_combo_2->GetValue().char_str()));
}


void ControlUI::onDevice2Enable(wxCommandEvent &event)
{
	_pApp->enableDevice(1,checkbox_2->GetValue());
}


void ControlUI::onDevice3NameChange(wxCommandEvent &event)
{
	_pApp->setName(2, string(name_combo_3->GetValue().char_str()));
}


void ControlUI::onDevice3Enable(wxCommandEvent &event)
{
	_pApp->enableDevice(2,checkbox_3->GetValue());
}


void ControlUI::onDevice4NameChange(wxCommandEvent &event)
{
	_pApp->setName(3, string(name_combo_4->GetValue().char_str()));
}


void ControlUI::onDevice4Enable(wxCommandEvent &event)
{
	_pApp->enableDevice(3,checkbox_4->GetValue());
}


// wxGlade: add ControlUI event handlers


void ControlUI::set_properties()
{
    // begin wxGlade: ControlUI::set_properties
    SetTitle(wxT("Panoptik Control"));
    SetSize(wxSize(758, 387));
    name_combo_1->SetSelection(0);
    checkbox_1->SetValue(1);
    name_combo_2->SetSelection(1);
    checkbox_2->SetValue(1);
    name_combo_3->SetSelection(2);
    checkbox_3->SetValue(1);
    name_combo_4->SetSelection(3);
    checkbox_4->SetValue(1);
    // end wxGlade
}


void ControlUI::do_layout()
{
    // begin wxGlade: ControlUI::do_layout
    wxBoxSizer* sizer_main = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_name_4 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_name_3 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_name_2 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_name_1 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_load_calibration = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_slave = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_master = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_save = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_save_all = new wxBoxSizer(wxHORIZONTAL);
    sizer_save_all->Add(save_all_action, 0, 0, 0);
    sizer_save_all->Add(save_all_path, 1, 0, 0);
    sizer_save_all->Add(save_all_browser, 0, 0, 0);
    sizer_main->Add(sizer_save_all, 0, wxEXPAND, 0);
    sizer_save->Add(save_action, 0, 0, 0);
    sizer_save->Add(save_path, 1, 0, 0);
    sizer_save->Add(save_browser, 0, 0, 0);
    sizer_main->Add(sizer_save, 0, wxEXPAND, 0);
    sizer_master->Add(master_action, 0, 0, 0);
    sizer_master->Add(master_port, 1, 0, 0);
    sizer_main->Add(sizer_master, 0, wxEXPAND, 0);
    sizer_slave->Add(slave_action, 0, 0, 0);
    sizer_slave->Add(slave_port, 1, 0, 0);
    sizer_slave->Add(slave_ip, 1, 0, 0);
    sizer_main->Add(sizer_slave, 0, wxEXPAND, 0);
    sizer_load_calibration->Add(load_calibration_action, 0, 0, 0);
    sizer_load_calibration->Add(load_calibration_path, 1, 0, 0);
    sizer_load_calibration->Add(load_calibration_browser, 0, 0, 0);
    sizer_main->Add(sizer_load_calibration, 0, wxEXPAND, 0);
    sizer_name_1->Add(device_label_1, 0, 0, 0);
    sizer_name_1->Add(name_combo_1, 0, 0, 0);
    sizer_name_1->Add(checkbox_1, 0, 0, 0);
    sizer_main->Add(sizer_name_1, 0, wxEXPAND, 0);
    sizer_name_2->Add(device_label_2, 0, 0, 0);
    sizer_name_2->Add(name_combo_2, 0, 0, 0);
    sizer_name_2->Add(checkbox_2, 0, 0, 0);
    sizer_main->Add(sizer_name_2, 0, wxEXPAND, 0);
    sizer_name_3->Add(device_label_3, 0, 0, 0);
    sizer_name_3->Add(name_combo_3, 0, 0, 0);
    sizer_name_3->Add(checkbox_3, 0, 0, 0);
    sizer_main->Add(sizer_name_3, 0, wxEXPAND, 0);
    sizer_name_4->Add(device_label_4, 0, 0, 0);
    sizer_name_4->Add(name_combo_4, 0, 0, 0);
    sizer_name_4->Add(checkbox_4, 0, 0, 0);
    sizer_main->Add(sizer_name_4, 0, wxEXPAND, 0);
    sizer_main->Add(panel_1, 1, wxEXPAND, 0);
    SetSizer(sizer_main);
    Layout();
    // end wxGlade
}



void ControlUI::setApp(Panoptik *pApp)
{
	_pApp = pApp;
	int n = _pApp->getNumDevices();
	if (n < 1) {
		name_combo_1->Disable();
		checkbox_1->Disable();
	}
	if (n < 2) {
		name_combo_2->Disable();
		checkbox_2->Disable();
	}
	if (n < 3) {
		name_combo_3->Disable();
		checkbox_3->Disable();
	}
	if (n < 4) {
		name_combo_4->Disable();
		checkbox_4->Disable();
	}
}

void copy_file_to_message(string filename, message_t &message)
{
	ifstream in_file;
	in_file.open(filename.c_str());
    int begin = in_file.tellg();
    in_file.seekg (0, ios::end);
    int end = in_file.tellg();
    int length = end-begin;
    in_file.seekg (0, ios::beg);
    message.rebuild(length);
    in_file.read((char *)message.data(),length);
}

void copy_message_to_file(message_t &message, string filename)
{
	ofstream o_file;
	o_file.open(filename.c_str());
	o_file.write((char *)message.data(), message.size());
	o_file.close();
}

void send_ack(socket_t *pSocket) {
	message_t acknowledgement (3);
	memcpy ((void *) acknowledgement.data (), "ACK", 3);
	pSocket->send(acknowledgement);
	cout << "sent ACK" << endl;
}

bool check_ack(socket_t *pSocket) {
	message_t acknowledgement;
	pSocket->recv(&acknowledgement);
	if (acknowledgement.size() == 3) {
		string msg((char *)acknowledgement.data(), acknowledgement.size());
		if (msg == "ACK") {
			cout << "received ACK" << endl;
			return (true);
		}
	}
	return (false);

}

bool check_end(message_t &message) {
	if (message.size() == 3) {
		string msg((char *)message.data(), message.size());
		if (msg == "END") {
			cout << "received END" << endl;
			return (true);
		}
	}
	return (false);
}

int ControlUI::getMinID() {
	int n = _pApp->getNumDevices();
	int min = 0;
	if (n > 0) {
		min = atoi(name_combo_1->GetValue().char_str());
	}
	if (n > 1) {
		int val = atoi(name_combo_2->GetValue().char_str());
		if (val < min) {
			min = val;
		}
	}
	if (n > 2) {
		int val = atoi(name_combo_3->GetValue().char_str());
		if (val < min) {
			min = val;
		}
	}
	if (n > 3) {
		int val = atoi(name_combo_4->GetValue().char_str());
		if (val < min) {
			min = val;
		}
	}
	cout << "min_val = " << min << endl;
	return min;
}

void ControlUI::threadSaveAllMaster() {

	string out_path(save_all_path->GetValue().char_str());
	bool terminated = false;
	try {
		message_t req_begin (3);
		memcpy ((void *) req_begin.data (), "SEQ", 3);
		_pSocket->send(req_begin);
		cout << "sent SEQ" << endl;

		message_t num_k_reply;
		_pSocket->recv (&num_k_reply);
		char num_k = *((char *)num_k_reply.data());

		_pApp->save(out_path,true);

		send_ack(_pSocket);

		for(int i = 0; i < num_k; ++i) {
			message_t mesh_reply;
			_pSocket->recv(&mesh_reply);
			if (check_end(mesh_reply)) {
				terminated = true;
				break;
			}
			stringstream mesh_name;
			mesh_name << out_path << i + _pApp->getNumDevices() << ".obj";
			copy_message_to_file(mesh_reply, mesh_name.str());
			send_ack(_pSocket);

			message_t img_reply;
			_pSocket->recv(&img_reply);
			if (check_end(img_reply)) {
				terminated = true;
				break;
			}
			stringstream img_name;
			img_name << out_path << i + _pApp->getNumDevices() << ".png";
			copy_message_to_file(img_reply, img_name.str());
			send_ack(_pSocket);
		}

		_pApp->start();
	}
	catch (zmq::error_t &error) {
		cout << "Network Error:" << endl;
		cout << error.what() << endl;
	}
	if (!terminated) {
		try {
			message_t response;
			_pSocket->recv (&response);
			string message((char *)response.data(), response.size());
			if (message == "END") {
				cout << "received END" << endl;
			}
			else {
				cout << "Unexpected Message:" << endl;
				if (message.size() < 30) {
					cout << message << endl;
				}
				else {
					cout << "Message too long to print" << endl;
				}
			}
		}
		catch (zmq::error_t &error) {
			cout << "Network Error:" << endl;
			cout << error.what() << endl;
		}
	}
	this->Enable();
	wxEndBusyCursor();
}


void ControlUI::threadRunSlave(){
    try
    {
        //  Socket to talk to clients
    	_pSocket = new socket_t(_pApp->getZMQContext(), ZMQ_REP);

        stringstream socket_def;
        socket_def << "tcp://" << slave_ip->GetValue().char_str() << ":" << slave_port->GetValue().char_str();
        cout << "Connecting to: " << socket_def.str() << endl;
        _pSocket->connect(socket_def.str().c_str());
        _netState = slave;

		while(1) {
			zmq::message_t request;
			_pSocket->recv (&request);
			string message((char *)request.data(), request.size());
			cout << "received " << message << std::endl;

			wxString tmpDir(wxT("/tmp/Panoptik"));
			if (!wxDirExists(tmpDir)) {
				wxMkdir(tmpDir);
			}

			if (message == "SEQ") {

				message_t num_k_reply (1);
				char num_k = _pApp->getNumDevices();
				memcpy ((void *) num_k_reply.data (), &num_k, 1);
				cout << "Sending number of Devices " << endl;

				_pApp->stop(); // Stop these Devices to reduce interference

				_pSocket->send(num_k_reply);

				check_ack(_pSocket);

				_pApp->save("/tmp/Panoptik/cal_",true); // Now the other side has finished, do a capture

				for (int i = 0; i < _pApp->getNumDevices(); ++i) {
					stringstream mesh_name;
					mesh_name << "/tmp/Panoptik/cal_" << i + getMinID()<< ".obj";
					message_t mesh_data;
					copy_file_to_message(mesh_name.str(), mesh_data);
					_pSocket->send(mesh_data);
					check_ack(_pSocket);


					stringstream img_name;
					img_name << "/tmp/Panoptik/cal_" << i + getMinID() << ".png";
					message_t img_data;
					copy_file_to_message(img_name.str(), img_data);
					_pSocket->send(img_data);
					check_ack(_pSocket);
				}

				_pApp->start();
			}
			else if (message == "SYN" ) {
				_pApp->save("/tmp/Panoptik/tmp.obj",false);

				message_t num_k_reply (1);
				char num_k = _pApp->getNumDevices();
				memcpy ((void *) num_k_reply.data (), &num_k, 1);
				cout << "Sending number of Devices " << endl;
				_pSocket->send(num_k_reply);
				check_ack(_pSocket);

				message_t mesh_data;
				copy_file_to_message("/tmp/Panoptik/tmp.obj", mesh_data);
				_pSocket->send(mesh_data);
				check_ack(_pSocket);

				for (int i = 0; i < _pApp->getNumDevices(); ++i) {
			        stringstream img_filename;
			        img_filename << "/tmp/Panoptik/tmp.obj_img_" << i + getMinID() << ".png";

					message_t img_data;
					copy_file_to_message(img_filename.str(), img_data);
					_pSocket->send(img_data);
					check_ack(_pSocket);
				}
			}
			else if (message == "END") {
				cout << "Server Disconnected" << endl;
				break;
			}
			else {
				cout << "unexpected request type" << endl;
			}

			message_t end_rep (3);
			memcpy((void *) end_rep.data (), "END", 3);
			cout << "sending END " << endl;
			_pSocket->send(end_rep);
		}
	}
	catch (zmq::error_t &error) {
		cout << "Network Error:" << endl;
		cout << error.what() << endl;
	}

	_pSocket->close();
	delete _pSocket;
	_pSocket = 0;
    _netState = solo;
	this->Enable();
	wxEndBusyCursor();
}

void ControlUI::threadSaveMaster(){

	string out_path(save_path->GetValue().char_str());
	bool terminated = false;
	try {
		message_t req_begin (3);
		memcpy ((void *) req_begin.data (), "SYN", 3);
		_pSocket->send(req_begin);
		cout << "sent SYN" << endl;

		_pApp->save(string(save_path->GetValue().char_str()),false);

		message_t num_k_reply;
		_pSocket->recv (&num_k_reply);
		char num_k = *((char *)num_k_reply.data());
		send_ack(_pSocket);

		message_t mesh_reply;
		_pSocket->recv(&mesh_reply);
		if (check_end(mesh_reply)) {
			terminated = true;
		}
		else {
			stringstream mesh_name;
			mesh_name << out_path << "_2_.obj";
			copy_message_to_file(mesh_reply, mesh_name.str());
			send_ack(_pSocket);

			for(int i = 0; i < num_k; ++i) {

				message_t img_reply;
				_pSocket->recv(&img_reply);
				if (check_end(img_reply)) {
					terminated = true;
					break;
				}
				stringstream img_name;
				img_name << out_path << "_img_" << i + _pApp->getNumDevices() << ".png";
				copy_message_to_file(img_reply, img_name.str());
				send_ack(_pSocket);
			}
		}
	}
	catch (zmq::error_t &error) {
		cout << "Network Error:" << endl;
		cout << error.what() << endl;
	}
	if (!terminated) {
		try {
			message_t response;
			_pSocket->recv (&response);
			string message((char *)response.data(), response.size());
			if (message == "END") {
				cout << "received END" << endl;
			}
			else {
				cout << "Unexpected Message:" << endl;
				if (message.size() < 30) {
					cout << message << endl;
				}
				else {
					cout << "Message too long to print" << endl;
				}
			}
		}
		catch (zmq::error_t &error) {
			cout << "Network Error:" << endl;
			cout << error.what() << endl;
		}
	}
	this->Enable();
	wxEndBusyCursor();
}
